package service

import (
	"context"
	"fmt"

	"github.com/pkg/errors"

	"bmember/internal/object"
	"bmember/pkg/util"

	"bmember/internal/model"

	"bmember/internal/dao"

	pb "bmember/api/pb/server"
	"bmember/pkg/lueerror"
)

type memberSrv struct {
	*Service
	name string
}

var mSrv *memberSrv

// InitMemberSrv  初始化会员相关逻辑层定义
func InitMemberSrv() {
	mSrv = &memberSrv{
		Service: service,
		name:    "member_service",
	}
}

// GetMemberSrv 获得会员相关逻辑层定义
func GetMemberSrv() *memberSrv {
	return mSrv
}

func (srv *memberSrv) findMemberChannel(ctx context.Context, brandId int64, openId, appId, unionId, channel string) (*model.LoginChannel, lueerror.LueError) {
	var mChan *model.LoginChannel
	var err error
	// 先根据 openid 查
	if openId != "" {
		mChan, err = dao.GetLoginChannelDao().GetMemberByOpenid(ctx, brandId, openId, appId, model.GetInnerChannel(channel))
		if err != nil {
			return nil, lueerror.DbErr(err)
		}
	} else if unionId != "" {
		// unionId 不为空且 openid 为空
		var mChans []*model.LoginChannel
		mChans, err = dao.GetLoginChannelDao().GetMemberByUnionId(ctx, brandId, unionId)
		if len(mChans) != 0 {
			// 任意选取一个
			mChan = mChans[0]
		}
	}
	return mChan, nil
}

func (srv *memberSrv) toFormatMemberInfoReply(mem *model.Member, mChan *model.LoginChannel) *pb.MemberInfoReply {
	if mem == nil {
		return nil
	}
	ret := &pb.MemberInfoReply{
		MemberId:   mem.Id,
		BrandId:    mem.BrandId,
		Birthday:   util.TimeToDate(mem.Birthday),
		Mobile:     mem.Mobile,
		Email:      mem.Email,
		Nickname:   mem.Nickname,
		IsFormal:   mem.IsFormal,
		Avatar:     mem.Avatar,
		IsRegister: true,
		Token:      "",
	}
	if mChan != nil {
		ret.OpenId = mChan.OpenId
		ret.AppId = mChan.AppId
	}
	return ret
}

// MemberLogin 用户登录
func (srv *memberSrv) MemberLogin(ctx context.Context, p *pb.MemberCheckReq) (*pb.MemberInfoReply, lueerror.LueError) {
	if p == nil {
		return nil, nil
	}
	mChan, errL := srv.findMemberChannel(ctx, p.BrandId, p.OpenId, p.AppId, "", p.Channel)
	if errL != nil {
		return nil, lueerror.DbErr(errL)
	}
	if mChan == nil {
		return &pb.MemberInfoReply{
			IsRegister: false,
		}, nil
	}
	mem, err := dao.GetMemberDao().GetMemberById(ctx, mChan.MemberId)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if !mem.IsValid() {
		return nil, lueerror.InvalidMember(errors.New("用户不允许登陆"))
	}
	return srv.toFormatMemberInfoReply(mem, mChan), nil
}

// PlatformLogin 直接使用平台code 登陆
func (srv *memberSrv) PlatformLogin(ctx context.Context, p *pb.MemberPlatformLoginReq) (*pb.MemberInfoReply, lueerror.LueError) {
	if p == nil {
		return nil, nil
	}
	openId, unionId, err := dao.GetBilinVendorDao().OpenPlatformLogin(ctx, p.AppId, p.Code, model.GetInnerChannel(p.Channel))
	if err != nil {
		return nil, lueerror.OpenErr(err)
	}
	req := &pb.MemberCheckReq{
		BrandId: p.BrandId,
		AppId:   p.AppId,
		OpenId:  openId,
		Channel: p.Channel,
	}
	ret, errL := srv.MemberLogin(ctx, req)
	if errL != nil {
		return nil, errL
	}
	if !ret.IsRegister {
		ret.OpenId = openId
		ret.UnionId = unionId
	}
	return ret, nil
}

func (srv *memberSrv) newLoginChannel(ctx context.Context, brandId, memberId int64, openId, appId, unionId, nickname, avatar, channel string) (*model.LoginChannel, lueerror.LueError) {
	var err error
	innerCh := model.GetInnerChannel(channel)
	if innerCh == 0 {
		return nil, lueerror.InvalidLoginChannel(errors.New("渠道枚举有误"))
	}
	mChan := model.NewLoginChannel(brandId, memberId, openId, appId, unionId, nickname, avatar, innerCh)
	mChan.Id, err = dao.GetLoginChannelDao().CreateLoginChannel(ctx, mChan)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	return mChan, nil
}

// 用户新增渠道
func (srv *memberSrv) addChannelForMember(ctx context.Context, memberId int64, chInfo *object.LoginChannelInfo) (*model.Member, *model.LoginChannel, lueerror.LueError) {
	mem, err := dao.GetMemberDao().GetMemberById(ctx, memberId)
	if err != nil {
		return nil, nil, lueerror.DbErr(err)
	}
	if mem == nil {
		return nil, nil, lueerror.InvalidMember(fmt.Errorf("脏数据，用户 %d 不存在", memberId))
	}
	// 新建渠道
	mChan, errL := srv.newLoginChannel(ctx, mem.BrandId, memberId, chInfo.OpenId, chInfo.AppId, chInfo.UnionId, chInfo.Nickname, chInfo.Avatar, chInfo.Channel)
	return mem, mChan, errL
}

// 已存在用户注册逻辑
func (srv *memberSrv) memberRegWithChannel(ctx context.Context, mChan *model.LoginChannel, chInfo *object.LoginChannelInfo) (*object.MemberWithChannel, lueerror.LueError) {
	if mChan == nil || chInfo == nil {
		return nil, nil
	}
	// 但渠道 openid 不同// 需要 新建渠道
	var mem *model.Member
	var err error
	if !mChan.IsSame(mChan.BrandId, chInfo.OpenId, chInfo.AppId, chInfo.Channel) {
		// 会员存，但是当前渠道未添加
		mem, mChan, err = srv.addChannelForMember(ctx, mChan.MemberId, chInfo)
		if err != nil {
			return nil, err.(lueerror.LueError)
		}
	} else {
		// 会员渠道都存在
		mem, err = dao.GetMemberDao().GetMemberById(ctx, mChan.MemberId)
		if err != nil {
			return nil, lueerror.DbErr(err)
		}
	}
	if mem == nil {
		return nil, lueerror.InvalidMember(errors.New("脏数据，用户不存"))
	}
	ret := object.NewMemberWithChannel(mem.Id, mem.BrandId, mChan.OpenId, mChan.AppId, mem.Mobile, mem.Email,
		mChan.Nickname, mChan.Avatar, mem.IsFormal, mem.Birthday)
	return ret, nil
}

// 创建新会员
// 传入参数必须是已经经过验证的
func (srv *memberSrv) newMemberReg(ctx context.Context, brandId int64, mInfo *object.MemberInfo, chInfo *object.LoginChannelInfo, regOrigin string) (*object.MemberWithChannel, lueerror.LueError) {
	if mInfo == nil || chInfo == nil {
		return nil, nil
	}
	// 创建用户
	ret, err := dao.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		var err error
		// 初始化会员
		mem := model.NewMember(brandId, mInfo.Nickname, mInfo.Mobile, mInfo.Email, mInfo.Gender, chInfo.Avatar, mInfo.CountryCode, mInfo.Province, mInfo.City, regOrigin, mInfo.Birthday)
		mem.Id, err = dao.GetMemberDao().CreateMember(ctx, mem)
		if err != nil {
			return nil, lueerror.DbErr(err)
		}
		// 创建渠道
		mChan, errL := srv.newLoginChannel(ctx, brandId, mem.Id, chInfo.OpenId, chInfo.AppId, chInfo.UnionId, chInfo.Nickname, chInfo.Avatar, chInfo.Channel)
		if errL != nil {
			return nil, errL
		}
		// 发送用户新增消息
		err = dao.GetMessageDao().SendMemberRegisterNotify(ctx, mem)
		if err != nil {
			return nil, lueerror.MqErr(err)
		}
		ret := object.NewMemberWithChannel(mem.Id, mem.BrandId, mChan.OpenId, mChan.AppId, mem.Mobile, mem.Email, mem.Nickname, mem.Avatar, mem.IsFormal, mem.Birthday)
		return ret, nil
	})
	if err != nil {
		return nil, parseLueerror(err)
	}
	return ret.(*object.MemberWithChannel), nil
}

// PlatMemberRegister 平台用户注册
func (srv *memberSrv) PlatMemberRegister(ctx context.Context, p *pb.MemberRegisterReq) (*pb.MemberInfoReply, lueerror.LueError) {
	if p == nil {
		return nil, nil
	}
	// 查询用户是否已经注册
	mChan, errL := srv.findMemberChannel(ctx, p.BrandId, p.OpenId, p.AppId, p.UnionId, p.Channel)
	if errL != nil {
		return nil, errL
	}
	chInfo := object.NewLoginChannelInfo(p.BrandId, 0, p.OpenId, p.AppId, p.UnionId, p.Nickname, p.Avatar, p.Channel)

	var mem *object.MemberWithChannel
	if mChan != nil {
		// 已注册,
		mem, errL = srv.memberRegWithChannel(ctx, mChan, chInfo)
		if errL != nil {
			return nil, errL
		}
	} else {
		// 注册新用户
		birthday, _ := util.DateParse(p.Birthday)
		mInfo := object.NewMemberInfo(p.BrandId, 0, p.Nickname, "", "", p.Gender, "86", p.Province, p.City, birthday)
		mem, errL = srv.newMemberReg(ctx, p.BrandId, mInfo, chInfo, p.Channel)
		if errL != nil {
			return nil, errL
		}
	}
	ret := &pb.MemberInfoReply{
		MemberId:   mem.MemberId,
		BrandId:    mem.BrandId,
		OpenId:     mem.OpenId,
		AppId:      mem.AppId,
		Birthday:   util.TimeToDate(mem.Birthday),
		Mobile:     mem.Mobile,
		Email:      mem.Email,
		Nickname:   mem.Nickname,
		IsFormal:   mem.IsFormal,
		Avatar:     mem.Avatar,
		IsRegister: true,
	}
	return ret, nil
}

// MemberUpdate 更新会员
func (srv *memberSrv) MemberUpdate(ctx context.Context, brandId, memberId int64, p *pb.MemberUpdateReq) (*pb.MemberInfoReply, lueerror.LueError) {
	if p == nil {
		return nil, nil
	}
	// 没有必要加悲观锁
	mem, errL := srv.getMemberById(ctx, brandId, memberId, false, false)
	if errL != nil {
		return nil, errL
	}
	birth, err := util.DateParse(p.Birthday)
	if err != nil {
		return nil, lueerror.InvalidRequest(fmt.Errorf("时间: %s 格式不正确", p.Birthday))
	}
	mem.UpdateInfo(p.Nickname, p.Avatar, p.Province, p.City, birth)
	mem.SetGender(p.Gender)
	err = dao.GetMemberDao().UpdateMemberById(ctx, mem)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	return srv.toFormatMemberInfoReply(mem, nil), nil
}

// 一个品牌目前来说只有一个体系
func (srv *memberSrv) getBrandGroup(ctx context.Context, brandId int64) (*model.AccountGroup, lueerror.LueError) {
	groups, err := dao.GetCardDao().GetAccountGroupByBrandId(ctx, brandId)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if len(groups) == 0 {
		return nil, lueerror.GroupNotExist(fmt.Errorf("体系不存在"))
	}
	return groups[0], nil
}

func (srv *memberSrv) getDefaultCard(ctx context.Context, groupId int64) (*model.AccountCard, lueerror.LueError) {
	dCard, err := dao.GetCardDao().GetDefaultCard(ctx, groupId)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if dCard == nil {
		return nil, lueerror.CardNotExist(fmt.Errorf("体系 %d 下无默认卡", groupId))
	}
	return dCard, nil
}

func (srv *memberSrv) getMemberAccount(ctx context.Context, memberId, groupId int64, canNull, isLock bool) (*model.MemberAccount, lueerror.LueError) {
	var acc *model.MemberAccount
	var err error
	if isLock {
		acc, err = dao.GetMemberAccountDao().LockAccByMemberGroup(ctx, memberId, groupId)
	} else {
		acc, err = dao.GetMemberAccountDao().GetAccByMemberGroup(ctx, memberId, groupId)
	}
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if acc == nil && !canNull {
		return nil, lueerror.AccNotExit(fmt.Errorf("会员 %d 体系 %d 下账号不存在", memberId, groupId))
	}
	return acc, nil
}

func (srv *memberSrv) getMemberAccInfoAgg(ctx context.Context, acc *model.MemberAccount) (*object.MemberAccAgg, lueerror.LueError) {
	if acc == nil {
		return nil, nil
	}
	card, err := dao.GetCardDao().GetCardAggByCardId(ctx, acc.CardId)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if card == nil {
		return nil, lueerror.DirtyData(fmt.Errorf("卡 %s 信息异常", acc.CardId))
	}
	accAgg := object.NewMemberAccAgg(acc.Id, acc.MemberId, acc.GroupId, acc.CardId,
		acc.Balance, acc.Integral, acc.Deposit)
	accAgg.SetCardInfo(card.Card.Name, card.Card.Cover, card.Card.CanRecharge, card.Card.CanIntegral,
		card.Card.GetTopupLimit(), card.Card.GetUnitIntegralAmount(), card.Card.Deposit)
	return accAgg, nil
}

func (srv *memberSrv) toFormatAccInfoReply(acc *object.MemberAccAgg) *pb.MemberAccInfoReply {
	if acc == nil {
		return nil
	}
	return &pb.MemberAccInfoReply{
		AccId:              acc.AccId,
		MemberId:           acc.MemberId,
		GroupId:            acc.GroupId,
		CardId:             acc.CardId,
		CardName:           acc.CardName,
		Cover:              acc.Cover,
		CanRecharge:        acc.CanRecharge,
		TopupLimit:         acc.TopupLimit,
		CanIntegral:        acc.CanIntegral,
		UnitIntegralAmount: acc.UnitIntegralAmount,
		Balance:            acc.Balance,
		Integral:           acc.Integral,
	}
}

// JoinMember 加入成为会员
func (srv *memberSrv) JoinMember(ctx context.Context, brandId, memberId int64, p *pb.MemberUpdateReq) (*pb.MemberAccInfoReply, lueerror.LueError) {
	if p == nil {
		return nil, nil
	}
	ret, err := dao.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		mem, errL := srv.getMemberById(ctx, brandId, memberId, false, true)
		if errL != nil {
			return nil, errL
		}
		if mem.Mobile == "" {
			return nil, lueerror.MobileRequired(nil)
		}
		// 获取品牌唯一体系
		group, errL := srv.getBrandGroup(ctx, brandId)
		if errL != nil {
			return nil, errL
		}
		// 判断会员是否已经领卡, 暂时每个品牌只有一个体系，
		acc, errL := srv.getMemberAccount(ctx, mem.Id, group.Id, true, false)
		if errL != nil {
			return nil, errL
		}
		if acc != nil {
			return acc, nil
		}
		// 获取默认卡
		dCard, errL := srv.getDefaultCard(ctx, group.Id)
		if errL != nil {
			return nil, errL
		}
		acc = model.NewMemberAccount(mem.BrandId, dCard.GroupId, dCard.Id, mem.Id, "")
		var err error
		acc.Id, err = dao.GetMemberAccountDao().CreateAccount(ctx, acc)
		if err != nil {
			return nil, lueerror.DbErr(err)
		}
		// 发送发卡消息
		err = dao.GetMessageDao().SendCardSendingNotify(ctx, acc)
		if err != nil {
			return nil, lueerror.MqErr(err)
		}
		// 将用户标记为会员
		if !mem.IsFormal {
			mem.BeFormal()
			err = dao.GetMemberDao().UpdateMemberFormalTag(ctx, mem)
			if err != nil {
				return nil, lueerror.DbErr(err)
			}
		}
		return acc, nil
	})
	if err != nil {
		return nil, parseLueerror(err)
	}
	accAgg, errL := srv.getMemberAccInfoAgg(ctx, ret.(*model.MemberAccount))
	if errL != nil {
		return nil, errL
	}
	return srv.toFormatAccInfoReply(accAgg), nil
}

// GetMemberAccount 获取账号信息
func (srv *memberSrv) GetMemberAccount(ctx context.Context, brandId, memberId int64) (*pb.MemberAccInfoReply, lueerror.LueError) {
	// 获取品牌唯一体系
	group, errL := srv.getBrandGroup(ctx, brandId)
	if errL != nil {
		return nil, errL
	}
	// 判断会员是否已经领卡
	acc, errL := srv.getMemberAccount(ctx, memberId, group.Id, true, false)
	if errL != nil {
		return nil, errL
	}
	accAgg, errL := srv.getMemberAccInfoAgg(ctx, acc)
	if errL != nil {
		return nil, errL
	}
	return srv.toFormatAccInfoReply(accAgg), nil
}

func (srv *memberSrv) getMemberById(ctx context.Context, brandId, memberId int64, canNull, isLock bool) (*model.Member, lueerror.LueError) {
	var mem *model.Member
	var err error
	if isLock {
		mem, err = dao.GetMemberDao().LockMemberById(ctx, memberId)
	} else {
		mem, err = dao.GetMemberDao().GetMemberById(ctx, memberId)
	}
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if mem == nil && canNull {
		return nil, nil
	}
	if mem == nil || mem.BrandId != brandId {
		return nil, lueerror.MemberNotExist(fmt.Errorf("会员%d不存在", memberId))
	}
	return mem, nil
}

func (srv *memberSrv) toFormatReplyAddress(addr *model.MemberAddress) *pb.MemberAddressReply {
	if addr == nil {
		return nil
	}
	return &pb.MemberAddressReply{
		AddrId:        addr.Id,
		MemberId:      addr.MemberId,
		ContactName:   addr.ContactName,
		ContactMobile: addr.ContactMobile,
		Gender:        addr.GetGender(),
		Title:         addr.Title,
		Address:       addr.Address,
		StreetNo:      addr.StreetNo,
		CityCode:      util.CityCodeWith156(addr.CityCode),
		Tag:           addr.Tag,
		Latitude:      addr.Latitude,
		Longitude:     addr.Longitude,
	}
}

// CreateMemberAddress 新增收货地址
func (srv *memberSrv) CreateMemberAddress(ctx context.Context, brandId, memberId int64, p *pb.MemberAddressCreateReq) (*pb.MemberAddressReply, lueerror.LueError) {
	if p == nil {
		return nil, nil
	}
	// 判断用户是否存在
	_, errL := srv.getMemberById(ctx, brandId, memberId, false, false)
	if errL != nil {
		return nil, errL
	}
	var err error
	addr := model.NewMemberAddress(brandId, memberId, p.ContactName, p.ContactMobile, p.Tag, model.GetInnerGender(p.Gender))
	addr.SetAddress(p.Address, p.StreetNo, util.CityCodeWithout156(p.CityCode), p.Latitude, p.Longitude)
	addr.Id, err = dao.GetMemberAddressDao().CreateAddress(ctx, addr)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	return srv.toFormatReplyAddress(addr), nil
}

// UpdateMemberAddress 更新收货地址
func (srv *memberSrv) UpdateMemberAddress(ctx context.Context, memberId int64, p *pb.MemberAddressUpdateReq) (*pb.MemberAddressReply, lueerror.LueError) {
	if p == nil {
		return nil, nil
	}
	addr, err := dao.GetMemberAddressDao().GetAddressById(ctx, p.AddrId)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if addr == nil || addr.MemberId != memberId {
		return nil, lueerror.InvalidAddress(fmt.Errorf("地址 %d 不存在", p.AddrId))
	}
	addr.UpdateInfo(p.ContactName, p.ContactMobile, p.Tag, model.GetInnerGender(p.Gender))
	addr.SetAddress(p.Address, p.StreetNo, util.CityCodeWithout156(p.CityCode), p.Latitude, p.Longitude)
	err = dao.GetMemberAddressDao().UpdateAddressById(ctx, addr)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	return srv.toFormatReplyAddress(addr), nil
}

// GetAddressList 获取地址列表
func (srv *memberSrv) GetAddressList(ctx context.Context, memberId int64) (*pb.MemberAddressListReply, lueerror.LueError) {
	addrs, err := dao.GetMemberAddressDao().GetAddressesByMember(ctx, memberId)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	rAddrs := make([]*pb.MemberAddressReply, len(addrs))
	for idx, addr := range addrs {
		rAddrs[idx] = srv.toFormatReplyAddress(addr)
	}
	return &pb.MemberAddressListReply{Addresses: rAddrs}, nil
}

// AddressSearch 地址搜索
func (srv *memberSrv) AddressSearch(ctx context.Context, address, region string, lat, lng float64) (*pb.AddressSearchReply, lueerror.LueError) {
	// 如果未传经纬度，先地址解析一次
	if lat == 0 || lng == 0 {
		lc, err := dao.GetBilinVendorDao().AddressToLocation(ctx, address, region)
		if err != nil {
			return nil, lueerror.BilinAdminErr(err)
		}
		if lc.Title == "" {
			return nil, lueerror.AddressNotExist(fmt.Errorf("腾讯 LBS 检索为空"))
		}
		lat = lc.Latitude
		lng = lc.Longitude
	}
	addrs, err := dao.GetBilinVendorDao().AddressSearch(ctx, address, lat, lng)
	if err != nil {
		return nil, lueerror.BilinAdminErr(err)
	}
	rAddrs := make([]*pb.AddressInfo, len(addrs))
	for idx, addr := range addrs {
		rAddrs[idx] = &pb.AddressInfo{
			Title:     addr.Title,
			Address:   addr.Address,
			Category:  addr.Category,
			Latitude:  addr.Latitude,
			Longitude: addr.Longitude,
			Distance:  addr.Distance,
			CityCode:  util.CityCodeWith156(addr.CityCode),
		}
	}
	return &pb.AddressSearchReply{
		Addresses: rAddrs,
	}, nil
}

// GetAddressByLocation 根据坐标获取地址信息
func (srv *memberSrv) GetAddressByLocation(ctx context.Context, lat, lng float64) (*pb.LocationToAddressReply, lueerror.LueError) {
	addr, err := dao.GetBilinVendorDao().LocationToAddress(ctx, lat, lng)
	if err != nil {
		return nil, lueerror.BilinAdminErr(err)
	}
	return &pb.LocationToAddressReply{
		CityCode: util.CityCodeWith156(addr.CityCode),
		Address:  addr.Address,
		Nation:   addr.Nation,
		Province: addr.Province,
		City:     addr.City,
		District: addr.District,
		Street:   addr.Street,
		StreetNo: addr.StreetNo,
	}, nil
}

// GetAddressLocation 地址解析
func (srv *memberSrv) GetAddressLocation(ctx context.Context, address, region string) (*pb.AddressToLocationReply, lueerror.LueError) {
	lc, err := dao.GetBilinVendorDao().AddressToLocation(ctx, address, region)
	if err != nil {
		return nil, lueerror.BilinAdminErr(err)
	}
	if lc.Title == "" {
		return nil, lueerror.AddressNotExist(fmt.Errorf("腾讯 LBS 检索为空"))
	}
	return &pb.AddressToLocationReply{
		Title:     lc.Title,
		Latitude:  lc.Latitude,
		Longitude: lc.Longitude,
		CityCode:  util.CityCodeWith156(lc.CityCode),
		Province:  lc.Province,
		City:      lc.City,
		District:  lc.District,
		Street:    lc.Street,
		StreetNo:  lc.StreetNo,
		Address:   fmt.Sprintf("%s%s%s%s%s%s", lc.Province, lc.City, lc.District, lc.Title, lc.Street, lc.StreetNo),
	}, nil
}

// DeleteAddress 删除地址
func (srv *memberSrv) DeleteAddress(ctx context.Context, memberId, addrId int64) lueerror.LueError {
	addr, err := dao.GetMemberAddressDao().GetAddressById(ctx, addrId)
	if err != nil {
		return lueerror.DbErr(err)
	}
	if addr == nil || addr.MemberId != memberId {
		return lueerror.InvalidAddress(fmt.Errorf("会员 %d 不存在地址 %d", memberId, addrId))
	}
	addr.Delete()
	err = dao.GetMemberAddressDao().DelAddressById(ctx, addr)
	if err != nil {
		return lueerror.DbErr(err)
	}
	return nil
}

func (srv *memberSrv) getMemberByMobile(ctx context.Context, brandId int64, mobile string, canNull bool) (*model.Member, lueerror.LueError) {
	mem, err := dao.GetMemberDao().GetMemberByMobile(ctx, brandId, mobile)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if !canNull && mem == nil {
		return nil, lueerror.MemberNotExist(fmt.Errorf("手机号未绑定会员"))
	}
	return mem, nil
}

// 会员合并，没有手机号的会员必须是没有资产的，否则无法合并成功
// targetMemberId 是合并的目标会员， memberId 的会员信息被抛弃
func (srv *memberSrv) mergeMember(ctx context.Context, brandId, targetMemberId, memberId int64) lueerror.LueError {
	// 被抛弃的会员必须无资产
	acc, errL := srv.getMemberAccount(ctx, brandId, memberId, true, false)
	if errL != nil {
		return errL
	}
	if acc != nil {
		// 报错采用手机号被使用形式提醒
		return lueerror.MobileHasUsed(fmt.Errorf("会员 %d 存在资产，无法合并", memberId))
	}
	// 锁定被合并会员
	abandonMem, errL := srv.getMemberById(ctx, brandId, memberId, false, true)
	if errL != nil {
		return errL
	}
	// 删除被合并会员
	_ = abandonMem.SetStatus(model.MemberMerged)
	err := dao.GetMemberDao().DeleteMemberById(ctx, abandonMem)
	if err != nil {
		return lueerror.DbErr(err)
	}
	// 合并的目标会员
	targetMem, errL := srv.getMemberById(ctx, brandId, targetMemberId, false, true)
	if errL != nil {
		return errL
	}
	// 记录合并信息,至目标会员
	targetMem.SetMergeInfo(memberId)
	err = dao.GetMemberDao().UpdateMemberById(ctx, targetMem)
	if err != nil {
		return lueerror.DbErr(err)
	}
	// 将被合并会员的登陆渠道合并到目标会员
	err = dao.GetLoginChannelDao().UpdateLoginChannelMember(ctx, brandId, abandonMem.Id, targetMem.Id)
	if err != nil {
		return lueerror.DbErr(err)
	}
	return nil
}

// 会员绑定手机号
func (srv *memberSrv) bindMobileForMember(ctx context.Context, brandId, memberId int64, mobile, countryCode string) (*model.Member, lueerror.LueError) {
	// 当前登陆会员
	curMem, errL := srv.getMemberById(ctx, brandId, memberId, false, false)
	if errL != nil {
		return nil, errL
	}
	// 已经绑定当前手机，返回成功
	if curMem.Mobile == mobile {
		return curMem, nil
	}
	// 查询手机号是否绑定其他用户
	mM, errL := srv.getMemberByMobile(ctx, brandId, mobile, true)
	if errL != nil {
		return nil, errL
	}
	ret, err := dao.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		// 手机号未被其他人使用, 直接更新
		if mM == nil {
			curMem.UpdateMobile(mobile, countryCode)
			err := dao.GetMemberDao().UpdateMobileEmail(ctx, curMem)
			if err != nil {
				return nil, lueerror.DbErr(err)
			}
			return curMem, nil
		}
		// 手机号已经被使用，将当前用户合并至有手机号用户
		errL = srv.mergeMember(ctx, brandId, mM.Id, curMem.Id)
		if errL != nil {
			return nil, errL
		}
		return mM, nil
	})
	if err != nil {
		return nil, parseLueerror(err)
	}
	return ret.(*model.Member), nil
}

// EncryptedMobileBinding 微信密文绑定手机号
func (srv *memberSrv) EncryptedMobileBinding(ctx context.Context, brandId, memberId int64, appId, openId, encryptedData, iv, bilinApp string) (*pb.MemberInfoReply, lueerror.LueError) {
	plat := model.GetInnerChannel(bilinApp)
	mobile, countryCode, err := dao.GetBilinVendorDao().GetMinaEncryptedMobile(ctx, appId, openId, encryptedData, iv, plat)
	if err != nil {
		return nil, lueerror.OpenErr(err)
	}
	mem, errL := srv.bindMobileForMember(ctx, brandId, memberId, mobile, countryCode)
	if errL != nil {
		return nil, errL
	}
	ret := srv.toFormatMemberInfoReply(mem, nil)
	return ret, nil
}

// MemberMobileBinding 用户绑定手机号
func (srv *memberSrv) MemberMobileBinding(ctx context.Context, brandId, memberId int64, mobile, codeType, code string) (*pb.MemberInfoReply, lueerror.LueError) {
	// 验证手机号
	if errL := srv.VerifyMobileCode(ctx, mobile, codeType, code); errL != nil {
		return nil, errL
	}
	mem, errL := srv.bindMobileForMember(ctx, brandId, memberId, mobile, "86")
	if errL != nil {
		return nil, errL
	}
	return srv.toFormatMemberInfoReply(mem, nil), nil
}

// MemberCardChanging 更换会员卡
func (srv *memberSrv) MemberCardChanging(ctx context.Context, brandId, memberId, cardId int64) lueerror.LueError {
	// 验证卡
	card, err := dao.GetCardDao().GetCardAggByCardId(ctx, cardId)
	if err != nil {
		return lueerror.DbErr(err)
	}
	if card == nil || card.Card.BrandId != brandId {
		return lueerror.CardNotExist(fmt.Errorf("卡 %d 不存在", cardId))
	}
	_, err = dao.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		acc, errL := srv.getMemberAccount(ctx, memberId, card.Group.Id, false, true)
		if errL != nil {
			return nil, errL
		}
		if acc.CardId == card.Card.Id {
			return nil, nil
		}
		acc.UpdateCard(card.Card.Id)
		err = dao.GetMemberAccountDao().UpdateAccCardById(ctx, acc)
		if err != nil {
			return nil, lueerror.DbErr(err)
		}
		return nil, nil
	})
	return parseLueerror(err)
}
